import { parse, type HTMLElement } from "node-html-parser";
import fs from "fs";
import pLimit from "p-limit";
import path from "path";
import cliProgress from "cli-progress";
// 引入封装
import { axiosInstance as axios, fileUtils } from "./axios.js";

main();

// ===================== 主函数 =====================
export async function main() {
  const { keywordsToProcess } = fileUtils.resumeCrawling();

  try {
    for (const keyword of keywordsToProcess) {
      // 【1】 根据关键词查询出html
      const htmlUrl = await getHtmlUrlByKeyword(keyword);
      if (!htmlUrl) continue;
      
      // 【2】解析html获取图片url并下载图片
      // https://www.51miz.com/so-tupian/88316.html

      // 遍历要爬取的图片类型
      for (const crawlType of fileUtils.config.types!) {
        if (!["tupian", "chahua", "beijing"].includes(crawlType)) {
          fileUtils.logError(`❌ 不支持的爬取类型：${crawlType}`);
          continue;
        }

        const htmlUrlWithCrawlType = htmlUrl.replace("tupian", crawlType!); // 将 tupian 替换成 crawlType
        console.log(
          `⭐ 正在解析【${keyword}-${crawlType}】的 html ... ${htmlUrlWithCrawlType}`
        );
        await getImgUrlsByHtmlAndDownload(htmlUrlWithCrawlType, keyword);
      }

      console.log(`✅ ${keyword}的图片下载完成！\n`);

      // 更新状态文件
      fileUtils.writeConfig({ lastProcessedKeyword: keyword });
    }
  } catch (error) {
    fileUtils.logError(error);
  }
}

// =====================【1】根据关键词查询出html=====================
async function getHtmlUrlByKeyword(keyword: string) {
  const res = await axios<{ word: string; url: string }[]>({
    url: "https://www.51miz.com/index.php?m=relate&ajax=1",
    method: "POST",
    headers: {
      accept: "application/json, text/javascript, */*; q=0.01",
      "content-type": "application/x-www-form-urlencoded; charset=UTF-8",
    },
    data: `pagesource=advice&plate_path=tupian&where_search=all&keyword=${encodeURIComponent(
      keyword
    )}`,
  });

  if (!res.data[0]?.url) {
    fileUtils.logError(
      `❌ 关键词 "${keyword}" 无搜索信息，建议从配置项中剔除！`
    );
    return "";
  }

  return res.data[0]?.url;
}

// =====================【2】解析html获取图片url=====================
async function getImgUrlsByHtmlAndDownload(
  htmlUrl: string,
  keyword: string,
  page = 0
) {
  const res = await axios.get(htmlUrl);
  const root = parse(res.data);

  // 第一页，先打印总共的图片数量
  if (page === 0) {
    const total = root.querySelector(
      ".list-description.enable-wrapper > span"
    )?.innerText;
    console.log(`📸 总共 ${total} 张图片\n`);
  }
  console.log(`✨ 正在解析并下载第 ${page} 页的图片...`);

  const imgUrls: string[] = [];
  const imgFatherDoms = root.querySelectorAll(".element-box-detail");
  imgFatherDoms.forEach((imgFatherDom: HTMLElement) => {
    const imgDom = imgFatherDom.querySelector(".image-box > img");
    let imgSrc = imgDom?.getAttribute("data-layzeload");
    if (!imgSrc) {
      fileUtils.logError(
        ` ${htmlUrl} 解析失败！可能没有该类型的图片！${imgFatherDom.innerHTML}`
      );
      return;
    }

    // src 属性没有， data-layzeload 属性有
    // data-layzeload="//img-qn-0.51miz.com/preview/photo/00/01/58/41/new-P-1584173-C99DE24F.jpg!/quality/90/unsharp/true/compress/true/format/webp/fh/260"
    // 开头补充 https:  去掉! 之后的字符串
    imgSrc = "https:" + imgSrc.split("!/")[0];
    imgUrls.push(imgSrc);
  });

  // 下载当前页的图片
  // 正则匹配出  https://www.51miz.com/so-tupian/88316.html 的 so-tupian 部分
  const type = htmlUrl.match(/so-(\w+)/)?.[1]!;
  await downloadImgs(keyword, type, imgUrls);

  // 查找是否有翻页，递归查询
  const nextPageUrl = root.querySelector(".next-page")?.getAttribute("href");
  if (nextPageUrl) {
    await new Promise((resolve) =>
      setTimeout(resolve, Math.random() * 1000 + 2000)
    ); // 给点延迟再查
    await getImgUrlsByHtmlAndDownload(nextPageUrl, keyword, page + 1);
  }
}

// ===================== 【3】下载图片到本地 =====================
async function downloadImgWithRetry(
  imgUrl: string,
  filePath: string,
  retries = 3
) {
  // 如果文件已经存在，则直接返回
  if (fs.existsSync(filePath)) {
    return;
  }

  try {
    const res = await axios.get(imgUrl, {
      responseType: "arraybuffer",
    });
    fs.writeFileSync(filePath, Buffer.from(res.data));
  } catch (error) {
    if (retries > 0) {
      console.log(`⚠️ 图片下载失败，重试中...`);
      await new Promise((resolve) =>
        setTimeout(resolve, Math.random() * 1000 + 1000)
      );
      return downloadImgWithRetry(imgUrl, filePath, retries - 1);
    } else {
      fileUtils.logError(error);
    }
  }
}

async function downloadImgs(keyword: string, type: string, imgUrls: string[]) {
  const dir = path.resolve(fileUtils.outDirPath, keyword, type);
  fs.mkdirSync(dir, { recursive: true }); //确保目录存在
  const limit = pLimit(fileUtils.config.concurrency); // 限制下载并发

  // 初始化进度条
  const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.rect);
  progressBar.start(imgUrls.length, 0);

  const promises = imgUrls.map(async (imgUrl, index) => {
    const fileName = path.basename(imgUrl); // 取 imgUrl 最后 / 之后的字符串作为文件名

    return limit(async () => {
      const filePath = `${dir}/${fileName}`;
      await downloadImgWithRetry(imgUrl, filePath);
      progressBar.increment(); // 更新进度条
    });
  });

  await Promise.all(promises);
  progressBar.stop(); // 停止进度条
}

